<!--
  Copyright 2018 Google LLC

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
-->
<link rel="import" href="../../bower_components/polymer/polymer.html">
<link rel="import" href="../../bower_components/app-route/app-location.html">
<link rel="import" href="../../bower_components/app-route/app-route.html">
<link rel="import" href="../../bower_components/iron-ajax/iron-ajax.html">
<link rel="import" href="../../bower_components/iron-icon/iron-icon.html">
<link rel="import" href="../../bower_components/paper-dialog/paper-dialog.html">
<link rel="import" href="../../bower_components/paper-spinner/paper-spinner.html">
<link rel="import" href="../../bower_components/paper-styles/color.html">
<link rel="import" href="settings-panel.html">
<link rel="import" href="stats-results.html">

<dom-module id="fuzzer-stats">
  <link rel="import" href="../../stylesheets/main.css" type="css">
  <link rel="import" href="../technology/technology.css" type="css">
  <template>
    <style>
      :host .error {
        background-color: var(--paper-red-50);
      }

      :host settings-panel {
        display: block;
        margin: 10px auto 20px;
      }

      #mainContent {
        margin: 20px 0px;
        padding: 0px 10px;
        width: 100%;
        max-width: 3000px;
      }
    </style>
    <app-toolbar>
      <paper-icon-button icon="menu" onclick="document.getElementById('drawer').toggle()"></paper-icon-button>
      <div id="title">Fuzzer Statistics</div>
      <template is="dom-if" if="[[loading]]">
        <paper-spinner active="[[loading]]"></paper-spinner>
      </template>
    </app-toolbar>
    <div id="mainContent">
      <app-location route="{{route}}"></app-location>
      <app-route route="{{route}}" pattern="[[prefix]]" tail="{{subroute}}"></app-route>
      <app-route route="{{subroute}}" pattern="/:group_by/date-start/:date_start/date-end/:date_end/fuzzer/:fuzzer" data="{{params}}" tail="{{jobSubroute}}"></app-route>
      <app-route route="{{subroute}}" pattern="/:group_by/fuzzer/:fuzzer" data="{{params}}" tail="{{jobSubroute}}"></app-route>
      <app-route route="{{jobSubroute}}" pattern="/job/:job" data="{{jobParams}}"></app-route>
      <app-route route="{{subroute}}" pattern="/:group_by/date-start/:date_start/date-end/:date_end/job/:job" data="{{params}}"></app-route>
      <app-route route="{{subroute}}" pattern="/:group_by/job/:job" data="{{params}}"></app-route>
      <iron-ajax
        id="ajax"
        url="/fuzzer-stats/load"
        method="POST"
        content-type="application/json"
        body="[[params]]"
        loading="{{loading}}"
        last-request="{{request}}"
        last-error="{{error}}"
        last-response="{{response}}"
        on-error="handleError"
        on-request="handleRequest"
        on-response="handleResponse"
        debounce-duration="500"></iron-ajax>
      <settings-panel
        loading="[[loading]]"
        fuzzers="[[info.fuzzers]]"
        jobs="[[info.jobs]]"
        params="[[params]]"
        settings="{{settings}}"
        on-submit-tapped="submitTapped"></settings-panel>
      <div id="errorContainer">
        <paper-dialog id="errorDialog" class="error" horizontal-align="auto" vertial-align="top">
          <p>Got error with status: [[responseError.status]]</p>
          <p>[[responseError.message]]</p>
        </paper-dialog>
      </div>
      <stats-results result="[[result]]"></stats-results>
    </div>
  </template>
  <script>
    class FuzzerStats extends Polymer.Element {
      static get is() { return 'fuzzer-stats'; }

      static get properties() {
        return {
          info: Object,
          params: Object,
          jobParams: Object,
          settings: {
            type: Object,
            value: () => { return {}; }
          },
          responseError: {
            type: Object,
            value: null,
          },
          prefix: String,
        };
      }

      static get observers() {
        return [
          'paramChanged(params.*)',
          'jobParamChanged(jobParams.*)',
          'routeChanged(route.path)',
        ];
      }

      ready() {
        super.ready();
        this.fillDefaultSettings();
      }

      fillDefaultSettings(settings) {
        if (!this.settings.fuzzer) {
          this.set('settings.fuzzer', '');
        }

        if (!this.settings.job) {
          this.set('settings.job', '');
       }

       // Show the date yesterday for Start Date and the date 7 days ago for
       // End Date if they are unset.
       this.set('settings.date_start', this.getDate(this.settings.date_start, 7));
       // Today's stats are not ready yet, so use yesterday's (this date is
       // inclusive).
       this.set('settings.date_end', this.getDate(this.settings.date_end, 1));
      }

      setParams(params) {
        this.set('settings.fuzzer', params.fuzzer);
        this.set('settings.job', params.job);
        this.set('settings.group_by', params.group_by);
        this.set('settings.date_start', params.date_start);
        this.set('settings.date_end', params.date_end);
        this.fillDefaultSettings();
      }

      paramChanged(cr) {
        this.setParams(cr.base);
        this.$.errorDialog.close();

        if (this.params.fuzzer && this.params.group_by) {
          this.$.ajax.generateRequest();
        }
      }

      jobParamChanged(cr) {
        this.set('params.job', cr.base.job);
      }

      routeChanged() {
        if (!this.route.path.startsWith(this.prefix)) {
          // navigating to a different page.
          location.reload();
        }
      }

      submitTapped() {
        var path = this._getURL(this.settings);
        this.set('route.path', path);
        this.$.errorDialog.close();
        this.$.ajax.generateRequest();
      }

      _getURL(settings) {
        var url = this.prefix + '/' + settings.group_by;

        if (settings.date_start === undefined) {
          console.assert(settings.date_end === undefined,
                         "Start date is undefined but end date is not.");
        } else {
          console.assert(settings.date_end !== undefined,
                         "End date is undefined but start date is not.");
          url += '/date-start/' + settings.date_start + '/date-end/' +
              settings.date_end;
        }

        if (settings.fuzzer) {
          url += '/fuzzer/' + settings.fuzzer;
        }

        if (settings.job) {
          url += '/job/' + settings.job;
        }

        return url;
      }

      handleRequest() {
        var reqs = this.$.ajax.activeRequests;
        for (let i=0;i<reqs.length;i++) {
          if (reqs[i] != this.request) {
            reqs[i].abort();
          }
        }

        this.error = null;
        this.result = null;
        this.responseError = null;
      }

      handleResponse() {
        this.processResponse(this.response);
        this.result = this.response;
        this.responseError = null;
      }

      handleError() {
        if (!this.error) {
          // this.error is undefined because its request is aborted.
          return;
        }

        this.result = null;
        this.responseError = this.error.response;
        this.$.errorDialog.positionTarget = this.$.errorContainer;
        this.$.errorDialog.open();
      }

      shouldShowError(error) {
        return !!error;
      }

      getDate(date_param, days_ago) {
        // Return a string representing a date. Returns the date |days_ago|
        // number of days ago if |date_param| isn't set.
        if (date_param)
          return date_param;

        var date_now = new Date();
        date_now.setDate(date_now.getDate() - days_ago);
        return date_now.toISOString().slice(0, 10);
      }

      processResponse(response) {
        // Make the first column a link if applicable.
        if (this.params.group_by == 'by-fuzzer' || this.params.group_by == 'by-job') {
          for (var row of response['rows']) {
            var firstCol = row['c'][0];
            var val = firstCol['v']

            var settings = {};
            settings.fuzzer = this.params.fuzzer;
            settings.job = this.params.job;
            settings.group_by = "by-day";
            settings.date_start = this.params.date_start;
            settings.date_end = this.params.date_end;

            if (this.params.group_by == 'by-fuzzer') {
              settings.fuzzer = val;
            } else {
              settings.job = val;
            }

            var url = this._getURL(settings);
            firstCol['f'] = `<a href="${url}">${val}</a>`;
          }
        }
      }
    }

    customElements.define(FuzzerStats.is, FuzzerStats);
  </script>
</dom-module>

